/*
 * Copyright (c) 2003 JGoodies Karsten Lentzsch. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *  o Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer. 
 *     
 *  o Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution. 
 *     
 *  o Neither the name of JGoodies Karsten Lentzsch nor the names of 
 *    its contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission. 
 *     
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

/*
 * Source code changes (c) 2005 beck et al. projects
 */

package edu.map.aamas.gui.docking;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Paint;
import java.awt.event.MouseListener;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;

/**
 * A lightweight component derived from JPanel that features certain subcomponents
 * that make it look similar to a frame or internal frame. It shows a titlebar above
 * its content component. The title bar has an
 * optional icon on the left, a title text and an optional toolbar on the right.
 */
public class SimpleInternalFrame extends JPanel {

	private static final long serialVersionUID = 1L;
	private JLabel titleLabel;
	private GradientPanel gradientPanel;
	private JPanel headerPanel;
	private boolean isSelected;

	// Instance Creation ****************************************************

	/**
	 * Constructs a <code>SimpleInternalFrame</code> with an empty title.
	 * 
	 * @param title the initial title
	 */    
	public SimpleInternalFrame() {
		this(null, "", null, null);
	}

	/**
	 * Constructs a <code>SimpleInternalFrame</code> with the specified title.
	 * 
	 * @param title the initial title
	 */
	public SimpleInternalFrame(String title) {
		this(null, title, null, null);
	}

	/**
	 * Constructs a <code>SimpleInternalFrame</code> with the specified icon, and title.
	 * 
	 * @param icon the initial icon
	 * @param title the initial title
	 */
	public SimpleInternalFrame(Icon icon, String title) {
		this(icon, title, null, null);
	}

	/**
	 * Constructs a <code>SimpleInternalFrame</code> with the specified title, tool bar, and content panel.
	 * 
	 * @param title the initial title
	 * @param bar the initial tool bar
	 * @param content the initial content pane
	 */
	public SimpleInternalFrame(String title, JToolBar bar, JComponent content) {
		this(null, title, bar, content);
	}

	/**
	 * Constructs a <code>SimpleInternalFrame</code> with the specified icon, title, tool bar, and content
	 * panel.
	 * 
	 * @param icon the initial icon
	 * @param title the initial title
	 * @param bar the initial tool bar
	 * @param content the initial content pane
	 */
	public SimpleInternalFrame(Icon icon, String title, JToolBar bar, JComponent content) {
		super(new BorderLayout());
		this.isSelected = false;
		this.titleLabel = new JLabel(title, icon, SwingConstants.LEADING);
		
		JPanel top = buildHeader(titleLabel, bar);

		add(top, BorderLayout.NORTH);
		if (content != null) {
			setContent(content);
		}
		setBorder(new ShadowBorder());
		setSelected(true);
		updateHeader();

	}

	// Public API ***********************************************************

	/**
	 * Returns the frame's icon.
	 * 
	 * @return the frame's icon
	 */
	public Icon getFrameIcon() {
		return titleLabel.getIcon();
	}

	/**
	 * Sets a new frame icon.
	 * 
	 * @param newIcon the icon to be set
	 */
	public void setFrameIcon(Icon newIcon) {
		Icon oldIcon = getFrameIcon();
		titleLabel.setIcon(newIcon);
		firePropertyChange("frameIcon", oldIcon, newIcon);
	}

	/**
	 * Returns the frame's title text.
	 * 
	 * @return String the current title text
	 */
	public String getTitle() {
		return titleLabel.getText();
	}

	/**
	 * Sets a new title text.
	 * 
	 * @param newText the title text tp be set
	 */
	public void setTitle(String newText) {
		String oldText = getTitle();
		titleLabel.setText(newText);
		firePropertyChange("title", oldText, newText);
	}

	/**
	 * Returns the current toolbar, null if none has been set before.
	 * 
	 * @return the current toolbar - if any
	 */
	public JToolBar getToolBar() {
		return headerPanel.getComponentCount() > 1 ? (JToolBar) headerPanel.getComponent(1) : null;
	}

	/**
	 * Sets a new tool bar in the header.
	 * 
	 * @param newToolBar the tool bar to be set in the header
	 */
	public void setToolBar(JToolBar newToolBar) {
		JToolBar oldToolBar = getToolBar();
		if (oldToolBar == newToolBar) {
			return;
		}
		if (oldToolBar != null) {
			headerPanel.remove(oldToolBar);
		}
		if (newToolBar != null) {
			newToolBar.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
			headerPanel.add(newToolBar, BorderLayout.EAST);
		}
		updateHeader();
		firePropertyChange("toolBar", oldToolBar, newToolBar);
	}

	/**
	 * Returns the content - null, if none has been set.
	 * 
	 * @return the current content
	 */
	public Component getContent() {
		return hasContent() ? getComponent(1) : null;
	}

	/**
	 * Sets a new panel content; replaces any existing content, if existing.
	 * 
	 * @param newContent the panel's new content
	 */
	public void setContent(Component newContent) {
		Component oldContent = getContent();
		if (hasContent()) {
			remove(oldContent);
		}
		add(newContent, BorderLayout.CENTER);
		firePropertyChange("content", oldContent, newContent);
	}

	/**
	 * Answers if the panel is currently selected (or in other words active) or not. In the selected state,
	 * the header background will be rendered differently.
	 * 
	 * @return boolean a boolean, where true means the frame is selected (currently active) and false means it
	 *         is not
	 */
	public boolean isSelected() {
		return isSelected;
	}

	/**
	 * This panel draws its title bar differently if it is selected, which may be used to indicate to the user
	 * that this panel has the focus, or should get more attention than other simple internal frames.
	 * 
	 * @param newValue a boolean, where true means the frame is selected (currently active) and false means it
	 *            is not
	 */
	public void setSelected(boolean newValue) {
		boolean oldValue = isSelected();
		isSelected = newValue;
		updateHeader();
		firePropertyChange("selected", oldValue, newValue);
	}

	public void addHeaderListener(MouseListener listener) {
		gradientPanel.addMouseListener(listener);
	}

	// Building *************************************************************

	/**
	 * Creates and answers the header panel, that consists of: an icon, a title label, a tool bar, and a
	 * gradient background.
	 * 
	 * @param label the label to paint the icon and text
	 * @param bar the panel's tool bar
	 * @return the panel's built header area
	 */
	private JPanel buildHeader(JLabel label, JToolBar bar) {
		gradientPanel = new GradientPanel(new BorderLayout(), getHeaderBackground());
		label.setOpaque(false);

		gradientPanel.add(label, BorderLayout.WEST);
		gradientPanel.setBorder(BorderFactory.createEmptyBorder(3, 4, 3, 1));

		headerPanel = new JPanel(new BorderLayout());
		headerPanel.add(gradientPanel, BorderLayout.CENTER);
		setToolBar(bar);
		headerPanel.setBorder(new RaisedHeaderBorder());
		headerPanel.setOpaque(false);
		return headerPanel;
	}

	/**
	 * Updates the header.
	 */
	private void updateHeader() {
		gradientPanel.setBackground(getHeaderBackground());
		gradientPanel.setOpaque(isSelected());
		titleLabel.setForeground(getTextForeground(isSelected()));
		headerPanel.repaint();

		Dimension minSize = new Dimension(headerPanel.getMinimumSize());
		minSize.height += 3; // border
		setMinimumSize(minSize);
	}

	/**
	 * Updates the UI. In addition to the superclass behavior, we need to update the header component.
	 */
	public void updateUI() {
		super.updateUI();
		if (titleLabel != null) {
			updateHeader();
		}
	}

	// Helper Code **********************************************************

	/**
	 * Checks and answers if the panel has a content component set.
	 * 
	 * @return true if the panel has a content, false if it's empty
	 */
	private boolean hasContent() {
		return getComponentCount() > 1;
	}

	/**
	 * Determines and answers the header's text foreground color. Tries to lookup a special color from the
	 * L&amp;F. In case it is absent, it uses the standard internal frame forground.
	 * 
	 * @param selected true to lookup the active color, false for the inactive
	 * @return the color of the foreground text
	 */
	protected Color getTextForeground(boolean selected) {
		Color c = UIManager.getColor(selected ? "SimpleInternalFrame.activeTitleForeground"
				: "SimpleInternalFrame.inactiveTitleForeground");
		if (c != null) {
			return c;
		}
		return UIManager.getColor(selected ? "InternalFrame.activeTitleForeground" : "Label.foreground");

	}

	/**
	 * Determines and answers the header's background color. Tries to lookup a special color from the L&amp;F.
	 * In case it is absent, it uses the standard internal frame background.
	 * 
	 * @return the color of the header's background
	 */
	protected Color getHeaderBackground() {
		Color c = UIManager.getColor("SimpleInternalFrame.activeTitleBackground");
		if (c != null)
			return c;
		c = UIManager.getColor("InternalFrame.activeTitleGradient");
		return c != null ? c : UIManager.getColor("InternalFrame.activeTitleBackground");
	}

	public Component getDragHandle() {
		return gradientPanel;
	}

	// Helper Classes *******************************************************

	// A custom border for the raised header pseudo 3D effect.
	private static class RaisedHeaderBorder extends AbstractBorder {

		private static final long serialVersionUID = 1L;
		private static final Insets INSETS = new Insets(1, 1, 1, 0);

		public Insets getBorderInsets(Component c) {
			return INSETS;
		}

		public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) {

			g.translate(x, y);
			g.setColor(UIManager.getColor("controlLtHighlight"));
			g.fillRect(0, 0, w, 1);
			g.fillRect(0, 1, 1, h - 1);
			g.setColor(UIManager.getColor("controlShadow"));
			g.fillRect(0, h - 1, w, 1);
			g.translate(-x, -y);
		}
	}

	// A panel with a horizontal gradient background.
	private static class GradientPanel extends JPanel {

		private static final long serialVersionUID = 1L;
		private GradientPanel(LayoutManager lm, Color background) {
			super(lm);
			setBackground(background);
		}

		public void paintComponent(Graphics g) {
			super.paintComponent(g);
			if (!isOpaque()) {
				return;
			}
			Color control = UIManager.getColor("control");
			int width = getWidth();
			int height = getHeight();

			Graphics2D g2 = (Graphics2D) g;
			Paint storedPaint = g2.getPaint();
			g2.setPaint(new GradientPaint(0, 0, getBackground(), width, 0, control));
			g2.fillRect(0, 0, width, height);
			g2.setPaint(storedPaint);
		}
	}

	private static class ShadowBorder extends AbstractBorder {
		private static final long serialVersionUID = 1L;
		
		private static final Insets INSETS = new Insets(1, 1, 3, 3);

		public Insets getBorderInsets(Component c) {
			return INSETS;
		}

		public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) {

			Color shadow = UIManager.getColor("controlShadow");
			if (shadow == null) {
				shadow = Color.GRAY;
			}
			Color lightShadow = new Color(shadow.getRed(), shadow.getGreen(), shadow.getBlue(), 170);
			Color lighterShadow = new Color(shadow.getRed(), shadow.getGreen(), shadow.getBlue(), 70);
			g.translate(x, y);

			g.setColor(shadow);
			g.fillRect(0, 0, w - 3, 1);
			g.fillRect(0, 0, 1, h - 3);
			g.fillRect(w - 3, 1, 1, h - 3);
			g.fillRect(1, h - 3, w - 3, 1);
			// Shadow line 1
			g.setColor(lightShadow);
			g.fillRect(w - 3, 0, 1, 1);
			g.fillRect(0, h - 3, 1, 1);
			g.fillRect(w - 2, 1, 1, h - 3);
			g.fillRect(1, h - 2, w - 3, 1);
			// Shadow line2
			g.setColor(lighterShadow);
			g.fillRect(w - 2, 0, 1, 1);
			g.fillRect(0, h - 2, 1, 1);
			g.fillRect(w - 2, h - 2, 1, 1);
			g.fillRect(w - 1, 1, 1, h - 2);
			g.fillRect(1, h - 1, w - 2, 1);
			g.translate(-x, -y);
		}

	}


}